#ifndef TRIANGLE_H
#define TRIANGLE_H

#include "global_object.h"
#include <cuda_runtime.h>
#include "Vertex.cu"
#include "Geometry.cu"
#include "GlobalObj.cu"
using namespace std;
class Triangle : public Geometry
{
public:
	Vertex* p1;
	Vertex* p2;
	Vertex* p3;
	inline __host__ __device__  Triangle();
	inline __host__ __device__  Triangle(Vertex* a, Vertex* b, Vertex* c){
		p1 = a;
		p2 = b;
		p3 = c;
	}
	inline __host__ __device__  ~Triangle();
	inline __device__ IntersectResult intersect(IntersectRequest& ir){

		IntersectResult result = IntersectResult();
		const Ray& ray = ir.ray;
		float tmin, tmax;
		tmin = ir.tMin;
		tmax = ir.tMax;
		float a, b, c, d, e, f, g, h, i;
		float j, k, l;
		float beta, gamma, t;
		//TODO make these references
		const Vector3& orig = ray.origin;
		const Vector3& dir = ray.direction;

		//<PAINFUL MATHS>
		a = p1->loc[0]-p2->loc[0]; //x1 -x2
		b = p1->loc[1]-p2->loc[1]; //y1 -y2
		c = p1->loc[2]-p2->loc[2]; //z1 -z2
		d = p1->loc[0]-p3->loc[0]; //x1 -x3
		e = p1->loc[1]-p3->loc[1]; //y1 -y3
		f = p1->loc[2]-p3->loc[2]; //z1 -z3
		g = -1.0* dir[0];
		h = -1.0* dir[1];
		i = -1.0* dir[2];
		j = p1->loc[0]- orig[0];
		k = p1->loc[1]- orig[1];
		l = p1->loc[2]- orig[2];

		float M = a*(e*i-h*f)+b*(g*f-d*i)+c*(d*h-e*g);

		t = (f*(a*k-j*b)+e*(j*c-a*l)+d*(b*l-k*c))/M; //plane intersection
		if(t<tmin | t > tmax)
		{
			return IntersectResult(false, 0.0f, Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f));
		}
		gamma = (i*(a*k-j*b)+h*(j*c-a*l)+g*(b*l-k*c))/M; //test p1 p2 edge
		if(gamma < 0 | gamma > 1)
		{
			return IntersectResult(false, 0.0f, Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f));
		}
		beta = (j*(e*i-h*f)+k*(g*f-d*i)+l*(d*h-e*g))/M; //test p1 p3 edge
		if(beta < 0 | beta > (1-gamma))
		{
			return IntersectResult(false, 0.0f, Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f));
		}
		//</PAINFUL MATHS>
		else
		{
			IntersectResult result = IntersectResult();
			result.hit = true;
			result.tVal = t;
			result.point = ir.ray.follow(t);       

			
			////interpolate the normal
			////gamma == 1 means p3
			////beta == 1 means p2
			////both == 0 means p1
			//result.normal = 
			//		(gamma*p3->normal 
			//		+ beta*p2->normal 
			//		+ (1-gamma-beta)*p1->normal);
			//ir.normal.normalize();
			

			//use perpendicular normals
			Vector3 normal;
			Vector3 E1 = p2->loc - p1->loc;
			Vector3 E2 = p3->loc - p2->loc;
			normal = E1.cross(E2);
			normal.normalize(); 
			result.normal = normal;

			//
			////interpolate texture coords
			//ir.textureCoords =
			//		(gamma*p3->textureMap
			//		+ beta*p2->textureMap
			//		+ (1-gamma-beta)*p1->textureMap);
			
			result.tex_coord = Vector3(0.0f,0.0f,0.0f);
        
			return result;
		}

	}

	inline __host__ __device__  void print(){

	} 

	inline __host__ void copyToDevice() {
		if(d_address == NULL){
			Triangle d_tri = Triangle(*this);
			if(p1->d_address == NULL){
				p1->copyToDevice();
			}
			if(p2->d_address == NULL){
				p2->copyToDevice();
			}
			if(p3->d_address == NULL){
				p3->copyToDevice();
			}
			d_tri.p1 = (Vertex*)p1->d_address;
			d_tri.p2 = (Vertex*)p2->d_address;
			d_tri.p3 = (Vertex*)p3->d_address;
			cudaMemcpy(d_address, &d_tri, sizeof(Triangle), cudaMemcpyHostToDevice);
		}
	}
};

#endif
